<?php
declare(strict_types=1);

namespace app\service;

class EncryptionService
{
    /**
     * 加密密钥
     * @var string
     */
    protected $encryptKey;
    
    /**
     * 构造函数
     * @param string|null $encryptKey 自定义加密密钥，为null时从环境变量获取
     */
    public function __construct(string $encryptKey = null)
    {
        $this->encryptKey = $encryptKey ?? env('WECHAT_ENCRYPT_KEY', '');
    }
    
    /**
     * 设置加密密钥
     * @param string $key 加密密钥
     * @return self
     */
    public function setEncryptKey(string $key): self
    {
        $this->encryptKey = $key;
        return $this;
    }
    
    /**
     * 获取加密密钥
     * @return string
     */
    public function getEncryptKey(): string
    {
        return $this->encryptKey;
    }
    
    /**
     * 生成初始向量
     * @param string $nonce 随机数
     * @param int $length 向量长度，默认16字节
     * @return string
     */
    public function generateIv(string $nonce, int $length = 16): string
    {
        return substr(md5($nonce), 0, $length);
    }
    
    /**
     * AES-256-CBC加密
     * @param string $data 待加密数据
     * @param string $nonce 随机数，用于生成初始向量
     * @param bool $base64Encode 是否进行base64编码，默认true
     * @return string 加密后的数据
     * @throws \Exception 加密失败时抛出异常
     */
    public function encrypt(string $data, string $nonce, bool $base64Encode = true): string
    {
        $iv = $this->generateIv($nonce);
        
        $encrypted = openssl_encrypt(
            $data,
            'AES-256-CBC',
            $this->encryptKey,
            OPENSSL_RAW_DATA,
            $iv
        );
        
        if ($encrypted === false) {
            throw new \Exception('加密失败: ' . openssl_error_string());
        }
        
        return $base64Encode ? base64_encode($encrypted) : $encrypted;
    }
    
    /**
     * AES-256-CBC解密
     * @param string $encryptedData 加密数据
     * @param string $nonce 随机数，用于生成初始向量
     * @param bool $base64Decode 是否需要先进行base64解码，默认true
     * @return string 解密后的数据
     * @throws \Exception 解密失败时抛出异常
     */
    public function decrypt(string $encryptedData, string $nonce, bool $base64Decode = true): string
    {
        if ($base64Decode) {
            $encryptedData = base64_decode($encryptedData);
            if ($encryptedData === false) {
                throw new \Exception('Base64解码失败');
            }
        }
        
        $iv = $this->generateIv($nonce);
        
        $decrypted = openssl_decrypt(
            $encryptedData,
            'AES-256-CBC',
            $this->encryptKey,
            OPENSSL_RAW_DATA,
            $iv
        );
        
        if ($decrypted === false) {
            throw new \Exception('解密失败: ' . openssl_error_string());
        }
        
        return $decrypted;
    }
    
    /**
     * 解密请求数据并解析为JSON
     * @param array $requestData 请求数据
     * @return array 解密后的数据
     */
    public function decryptRequest(array $requestData): array
    {
        // 检查必要参数
        if (!isset($requestData['data']) || !isset($requestData['timestamp']) || 
            !isset($requestData['nonce']) || !isset($requestData['sign'])) {
            return ['error' => '缺少必要参数'];
        }
        
        // 验证签名和时间戳等逻辑可以根据实际需求添加
        
        // 解密data字段
        try {
            // 解密数据
            $decryptedData = $this->decrypt($requestData['data'], $requestData['nonce']);
            
            // 解析JSON数据
            $jsonData = json_decode($decryptedData, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                return ['error' => '解析JSON数据失败: ' . json_last_error_msg()];
            }
            
            return $jsonData;
        } catch (\Exception $e) {
            return ['error' => '解密数据失败: ' . $e->getMessage()];
        }
    }
    
    /**
     * 加密响应数据
     * @param array|string $data 待加密数据
     * @param string $nonce 随机数
     * @return array 加密后的响应数据
     */
    public function encryptResponse($data, string $nonce): array
    {
        $timestamp = time();
        
        try {
            // 如果是数组，先转为JSON
            if (is_array($data)) {
                $jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
                if ($jsonData === false) {
                    return ['error' => '数据JSON编码失败: ' . json_last_error_msg()];
                }
            } else {
                $jsonData = $data;
            }
            
            // 加密数据
            $encryptedData = $this->encrypt($jsonData, $nonce);
            
            // 构建响应
            $response = [
                'data' => $encryptedData,
                'timestamp' => $timestamp,
                'nonce' => $nonce,
                // 这里可以添加签名生成逻辑
                'sign' => $this->generateSign($encryptedData, $timestamp, $nonce)
            ];
            
            return $response;
        } catch (\Exception $e) {
            return ['error' => '加密响应数据失败: ' . $e->getMessage()];
        }
    }
    
    /**
     * 生成签名
     * @param string $data 数据
     * @param int $timestamp 时间戳
     * @param string $nonce 随机数
     * @return string 签名
     */
    protected function generateSign(string $data, int $timestamp, string $nonce): string
    {
        // 签名生成逻辑，可以根据实际需求实现
        // 示例：使用HMAC-SHA256算法
        $signStr = $data . $timestamp . $nonce;
        return hash_hmac('sha256', $signStr, $this->encryptKey);
    }
    
    /**
     * 验证签名
     * @param array $requestData 请求数据
     * @return bool 签名是否有效
     */
    public function verifySign(array $requestData): bool
    {
        if (!isset($requestData['data']) || !isset($requestData['timestamp']) || 
            !isset($requestData['nonce']) || !isset($requestData['sign'])) {
            return false;
        }
        
        $data = $requestData['data'];
        $timestamp = $requestData['timestamp'];
        $nonce = $requestData['nonce'];
        $sign = $requestData['sign'];
        
        $expectedSign = $this->generateSign($data, $timestamp, $nonce);
        
        return hash_equals($expectedSign, $sign);
    }
}